package com.baixiaowen.xiaoaointerview.并发提升篇.并发环境下单例怎么写性能最高;


import java.util.concurrent.atomic.AtomicReference;

public class Foo {

    static class DbConnection {
        DbConnection() {
            System.err.println("Thread+" + Thread.currentThread().getId() + " is initializing");
        }
    }

    static volatile DbConnection ref;

    // monitor -> spinlock -> wait set ->
    public synchronized static DbConnection getDb1() {
        if (ref == null) {
            ref = new DbConnection();
        }
        return ref;
    }

    /**
     * 双重检查锁
     * monitor -> spinlock -> wait set ->
     */
    public static DbConnection getDb2() {
        /**
         * volatile 会把它周围的所有指令重排序都禁止的
         *      A
         *      B
         *      volatile read / write
         *      C
         *      D
         *
         * 不会出现 B A ... D C 这种情况了，所以说volatile的约束还是相对很强
         */

        if (ref == null) {
            synchronized (Foo.class) {
                System.err.println("Synchronized block pass thread: " + Thread.currentThread().getId());
                if (ref == null) {
                    ref = new DbConnection();
                }
            }
        }
        return ref;
    }

    /**
     * Java 9 提供的，当前的项目的版本是Java8
     * 使用更轻的约束条件
     *
     * AtomicReference.getAcquire() 就是happens-before的约束，保证发生在它之间的指令，一定会在它之前执行
     *
     * weak atomics : acquire , release
     */
    static AtomicReference<DbConnection> refe = new AtomicReference<>();

//    public static DbConnection getDb3() {
//
//        // java9
//
//        var localRef = refe.getAcquire();
//        if (localRef == null) {
//
//            // 这里可以用CAS操作来替换synchronized，思想是好的，但是对这个场景不好，因为这里每个线程都会创建一个对象，这本身开销很大
////            var newObj = new DbConnection();
////            refe.compareAndSet(null, newObj);
//
//            synchronized (Foo.class) {
//                System.err.println("Synchronized block pass thread: " + Thread.currentThread().getId());
//                localRef = refe.getAcquire();
//                if (localRef == null) {
//                    refe.setRelease(new DbConnection());
//                }
//            }
//        }
//
//        return localRef;
//    }

    public static void main(String[] args) {
        for (int i = 0; i < 200; i++) {
            new Thread(() -> {
                getDb2();
            }).start();
        }
    }

}
